home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Express Pd: GALORE
/
Express Pd Galore - The Amiga PD & Shareware CD (1994)(Express Pd)[!][Amiga-CD32-CDTV].iso
/
productivity
/
term
/
termsound.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-16
|
21KB
|
1,066 lines
/*
** termSound.c
**
** Sound support routines
**
** Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
** All Rights Reserved
*/
#include "termGlobal.h"
/* Double buffering chunk size. */
#define BUFFER_SIZE 32768
/* Maximum replay volume. */
#define MAX_VOLUME 64
/* Stereo/channel sample type. */
typedef LONG SampleType;
/* Channel definitions. */
#define SAMPLE_LEFT 2
#define SAMPLE_RIGHT 4
#define SAMPLE_STEREO 6
/* A fixed-point value, 16 bits to the left of
* the point and 16 to the right. A Fixed is a
* number of 2**16ths, i.e. 65536ths.
*/
typedef LONG Fixed;
/* Unity = Fixed 1.0 = maximum volume */
#define Unity 0x10000L
/* Sample compression modes. */
#define sCmpNone 0
#define sCmpFibDelta 1
/* The voice header. */
typedef struct
{
ULONG oneShotHiSamples, /* # samples in the high octave 1-shot part */
repeatHiSamples, /* # samples in the high octave repeat part */
samplesPerHiCycle; /* # samples/cycle in high octave, else 0 */
UWORD samplesPerSec; /* data sampling rate */
UBYTE ctOctave, /* # of octaves of waveforms */
sCompression; /* data compression technique used */
Fixed volume; /* playback nominal volume from 0 to Unity
* (full volume). Map this value into
* the output hardware's dynamic range.
*/
} Voice8Header;
/* Double-buffering information. */
struct BufferInfo
{
LONG Size;
UBYTE Buffer[BUFFER_SIZE];
};
/* Sound replay information. */
struct SoundInfo
{
UBYTE Name[MAX_FILENAME_LENGTH];
ULONG Rate,
Length,
Volume;
APTR SoundData;
APTR LeftData,
RightData;
};
/* Local data. */
STATIC struct SignalSemaphore *SoundSemaphore;
STATIC struct SoundInfo *SoundSlot[SOUND_COUNT];
/* DeltaUnpack(UBYTE *Src,ULONG Size,BYTE *Dst):
*
* Unpack Fibonacci-delta-encoded data.
*/
STATIC VOID __regargs
DeltaUnpack(UBYTE *Src,ULONG Size,BYTE *Dst)
{
STATIC BYTE CodeToDelta[16] = { -34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21 };
BYTE Value = (BYTE)Src[1];
UBYTE Code;
/* Skip the header information. */
Src += 2;
Size -= 2;
/* Run down the chunk... */
while(Size--)
{
Code = *Src++;
/* Add the top nibble delta. */
Value += CodeToDelta[Code >> 4];
*Dst++ = Value;
/* Add the bottom nibble delta. */
Value += CodeToDelta[Code & 0xF];
*Dst++ = Value;
}
}
/* FreeSound(struct SoundInfo *SoundInfo):
*
* Free sound handle and associated data.
*/
VOID __regargs
FreeSound(struct SoundInfo *SoundInfo)
{
FreeVec(SoundInfo -> SoundData);
FreeVec(SoundInfo);
}
/* LoadSound(STRPTR Name):
*
* Load a sound file from disk.
*/
struct SoundInfo * __regargs
LoadSound(STRPTR Name,BYTE Warn)
{
struct SoundInfo *SoundInfo = NULL;
struct IFFHandle *Handle;
/* Allocate IFF handle for reading. */
if(Handle = AllocIFF())
{
/* Open a standard DOS stream. */
if(Handle -> iff_Stream = Open(Name,MODE_OLDFILE))
{
/* Say it's a DOS stream. */
InitIFFasDOS(Handle);
/* Open the file for reading. */
if(!OpenIFF(Handle,IFFF_READ))
{
ULONG SoundStops[3 * 2] =
{
ID_8SVX,ID_VHDR,
ID_8SVX,ID_CHAN,
ID_8SVX,ID_BODY
};
/* Mark the chunks to stop at. */
if(!StopChunks(Handle,SoundStops,3))
{
struct ContextNode *Chunk;
Voice8Header Header;
UBYTE Compression;
SampleType Channel = SAMPLE_STEREO;
BYTE SingleChannel = TRUE;
/* Clear the voice header. */
memset(&Header,0,sizeof(Voice8Header));
/* Scan for data... */
while(!ParseIFF(Handle,IFFPARSE_SCAN))
{
Chunk = CurrentChunk(Handle);
/* What did we find? */
switch(Chunk -> cn_ID)
{
/* Looks like the basic voice header. */
case ID_VHDR:
/* Read the header. */
if(ReadChunkRecords(Handle,&Header,MIN(Chunk -> cn_Size,sizeof(Voice8Header)),1) == 1)
{
/* Compression type supported? */
if(Header . sCompression == sCmpNone || Header . sCompression == sCmpFibDelta)
{
/* Allocate the sound handle. */
if(SoundInfo = (struct SoundInfo *)AllocVec(sizeof(struct SoundInfo),MEMF_ANY | MEMF_CLEAR))
{
/* Install the rate, volume and length. */
SoundInfo -> Rate = SysBase -> ex_EClockFrequency * 5 / Header . samplesPerSec;
SoundInfo -> Length = Header . oneShotHiSamples ? Header . oneShotHiSamples : Header . repeatHiSamples;
SoundInfo -> Volume = (Header . volume * MAX_VOLUME) / Unity;
/* Remember compression mode. */
Compression = Header . sCompression;
}
}
}
break;
/* Looks like sound channel information. */
case ID_CHAN:
/* Do we have a handle to manage it? */
if(SoundInfo)
{
/* Read the channel information. */
if(ReadChunkRecords(Handle,&Channel,MIN(Chunk -> cn_Size,sizeof(SampleType)),1) == 1)
{
/* Stereo sound file? */
if(Channel == SAMPLE_STEREO)
SingleChannel = FALSE;
}
else
{
FreeVec(SoundInfo);
SoundInfo = NULL;
}
}
break;
/* Looks like the sound body data. */
case ID_BODY:
/* Do we have sound handle to manage it? */
if(SoundInfo)
{
BYTE Success = FALSE;
/* Uncompressed raw data? */
if(Compression == sCmpNone)
{
ULONG Length = MIN(Chunk -> cn_Size,SoundInfo -> Length);
/* Allocate a buffer. */
if(SoundInfo -> SoundData = AllocVec(Length,MEMF_ANY))
{
/* Read the data. */
if(ReadChunkRecords(Handle,SoundInfo -> SoundData,Length,1) == 1)
{
/* Store the actual length. */
SoundInfo -> Length = Length;
Success = TRUE;
}
else
FreeVec(SoundInfo -> SoundData);
}
}
else
{
ULONG Length = Chunk -> cn_Size;
UBYTE *TempBuffer;
/* Allocate a temporary decompression buffer. */
if(TempBuffer = (UBYTE *)AllocVec(Length,MEMF_ANY))
{
/* Read the compressed data. */
if(ReadChunkRecords(Handle,TempBuffer,Length,1) == 1)
{
/* Allocate space for the uncompressed data. */
if(SoundInfo -> SoundData = AllocVec((Length - 2) * 2,MEMF_ANY))
{
/* Stereo sound file? */
if(!SingleChannel && Channel == SAMPLE_STEREO)
{
UBYTE *Data = SoundInfo -> SoundData;
/* Unpack the stereo sound. */
DeltaUnpack(TempBuffer, Length / 2,Data);
DeltaUnpack(TempBuffer + (Length / 2),Length / 2,Data + (Length - 4));
/* Remember the sound length. */
SoundInfo -> Length = (Length - 4) * 2;
}
else
{
/* Unpack the mono sound. */
DeltaUnpack(TempBuffer,Length,SoundInfo -> SoundData);
/* Remember the sound length. */
SoundInfo -> Length = (Length - 2) * 2;
}
Success = TRUE;
}
}
FreeVec(TempBuffer);
}
}
if(!Success)
{
FreeVec(SoundInfo);
SoundInfo = NULL;
}
}
break;
}
}
/* Did we get what we wanted? */
if(SoundInfo)
{
/* Any sound data allocated? */
if(SoundInfo -> SoundData)
{
UBYTE *Data = SoundInfo -> SoundData;
/* Which kind of sound file did
* we read?
*/
switch(Channel)
{
/* Left channel only. */
case SAMPLE_LEFT:
SoundInfo -> LeftData = Data;
break;
/* Right channel only. */
case SAMPLE_RIGHT:
SoundInfo -> RightData = Data;
break;
/* Two stereo channels. */
case SAMPLE_STEREO:
/* One sound mapped to two voices. */
if(SingleChannel)
SoundInfo -> LeftData = SoundInfo -> RightData = Data;
else
{
/* Split the voice data. */
SoundInfo -> Length = SoundInfo -> Length / 2;
SoundInfo -> LeftData = Data;
SoundInfo -> RightData = Data + SoundInfo -> Length;
}
break;
}
}
else
{
FreeVec(SoundInfo);
SoundInfo = NULL;
}
}
}
CloseIFF(Handle);
}
Close(Handle -> iff_Stream);
}
FreeIFF(Handle);
}
/* Successful? */
if(SoundInfo)
strcpy(SoundInfo -> Name,Name);
else
{
if(Warn)
MyEasyRequest(Window,LocaleString(MSG_TERMSOUND_COULD_NOT_LOAD_SOUND_FILE_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),Name);
}
return(SoundInfo);
}
/* ReplaySound():
*
* Replay sound with given information (doubly-buffered).
*/
STATIC VOID __inline
ReplaySound(struct SoundInfo *SoundInfo,struct IOAudio *SoundControlRequest,struct IOAudio *SoundRequestLeft,struct IOAudio *SoundRequestRight,struct BufferInfo *BufferInfo)
{
UBYTE *Left = SoundInfo -> LeftData,
*Right = SoundInfo -> RightData;
ULONG Size = SoundInfo -> Length,Length;
WORD Base = 0;
Length = MIN(BUFFER_SIZE,Size);
Size -= Length;
/* Left channel available? */
if(!(((ULONG)SoundRequestLeft -> ioa_Request . io_Unit) & (LEFT0F | LEFT1F)))
Left = NULL;
/* Right channel available? */
if(!(((ULONG)SoundRequestRight -> ioa_Request . io_Unit) & (RIGHT0F | RIGHT1F)))
Right = NULL;
/* Fill up left buffer. */
if(Left)
{
CopyMem(Left,BufferInfo[Base] . Buffer,Length);
BufferInfo[Base] . Size = Length;
Left += Length;
}
/* Fill up right buffer. */
if(Right)
{
CopyMem(Right,BufferInfo[Base + 2] . Buffer,Length);
BufferInfo[Base + 2] . Size = Length;
Right += Length;
}
/* Process sound data. */
do
{
/* Block both channels. */
SoundControlRequest -> ioa_Request . io_Command = CMD_STOP;
SendIO(SoundControlRequest);
WaitIO(SoundControlRequest);
/* Any data for the left channel? */
if(Left)
{
SoundRequestLeft -> ioa_Request . io_Command = CMD_WRITE;
SoundRequestLeft -> ioa_Request . io_Flags = ADIOF_PERVOL;
SoundRequestLeft -> ioa_Period = SoundInfo -> Rate;
SoundRequestLeft -> ioa_Volume = SoundInfo -> Volume;
SoundRequestLeft -> ioa_Cycles = 1;
SoundRequestLeft -> ioa_Data = BufferInfo[Base] . Buffer;
SoundRequestLeft -> ioa_Length = BufferInfo[Base] . Size;
/* Get the left channel going. */
BeginIO(SoundRequestLeft);
}
/* Any data for the right channel? */
if(Right)
{
SoundRequestRight -> ioa_Request . io_Command = CMD_WRITE;
SoundRequestRight -> ioa_Request . io_Flags = ADIOF_PERVOL;
SoundRequestRight -> ioa_Period = SoundInfo -> Rate;
SoundRequestRight -> ioa_Volume = SoundInfo -> Volume;
SoundRequestRight -> ioa_Cycles = 1;
SoundRequestRight -> ioa_Data = BufferInfo[Base + 2] . Buffer;
SoundRequestRight -> ioa_Length = BufferInfo[Base + 2] . Size;
/* Get the right channel going. */
BeginIO(SoundRequestRight);
}
/* Start up both channels synchronously... */
SoundControlRequest -> ioa_Request . io_Command = CMD_START;
SendIO(SoundControlRequest);
WaitIO(SoundControlRequest);
/* Grab the other buffers. */
Base ^= 1;
/* Still any data left? */
if(Size)
{
/* Cut off the next slice. */
Length = MIN(BUFFER_SIZE,Size);
Size -= Length;
/* Left channel available? */
if(Left)
{
CopyMem(Left,BufferInfo[Base] . Buffer,Length);
BufferInfo[Base] . Size = Length;
Left += Length;
}
/* Right channel available? */
if(Right)
{
CopyMem(Right,BufferInfo[Base + 2] . Buffer,Length);
BufferInfo[Base + 2] . Size = Length;
Right += Length;
}
/* Last slice eaten? */
if(!Size)
{
/* Wait for sounds to terminate. */
if(Left)
WaitIO(SoundRequestLeft);
if(Right)
WaitIO(SoundRequestRight);
/* Block both channels. */
SoundControlRequest -> ioa_Request . io_Command = CMD_STOP;
SendIO(SoundControlRequest);
WaitIO(SoundControlRequest);
/* Any data for the left channel? */
if(Left)
{
SoundRequestLeft -> ioa_Request . io_Command = CMD_WRITE;
SoundRequestLeft -> ioa_Request . io_Flags = ADIOF_PERVOL;
SoundRequestLeft -> ioa_Period = SoundInfo -> Rate;
SoundRequestLeft -> ioa_Volume = SoundInfo -> Volume;
SoundRequestLeft -> ioa_Cycles = 1;
SoundRequestLeft -> ioa_Data = BufferInfo[Base] . Buffer;
SoundRequestLeft -> ioa_Length = BufferInfo[Base] . Size;
/* Get the left channel going. */
BeginIO(SoundRequestLeft);
}
/* Any data for the right channel? */
if(Right)
{
SoundRequestRight -> ioa_Request . io_Command = CMD_WRITE;
SoundRequestRight -> ioa_Request . io_Flags = ADIOF_PERVOL;
SoundRequestRight -> ioa_Period = SoundInfo -> Rate;
SoundRequestRight -> ioa_Volume = SoundInfo -> Volume;
SoundRequestRight -> ioa_Cycles = 1;
SoundRequestRight -> ioa_Data = BufferInfo[Base + 2] . Buffer;
SoundRequestRight -> ioa_Length = BufferInfo[Base + 2] . Size;
/* Get the right channel going. */
BeginIO(SoundRequestRight);
}
/* Start up both channels synchronously... */
SoundControlRequest -> ioa_Request . io_Command = CMD_START;
SendIO(SoundControlRequest);
WaitIO(SoundControlRequest);
}
}
/* Wait for sounds to terminate. */
if(Left)
WaitIO(SoundRequestLeft);
if(Right)
WaitIO(SoundRequestRight);
}
while(Size);
}
/* PlaySound(struct SoundInfo *SoundInfo):
*
* Replay a sound.
*/
VOID __regargs
PlaySound(struct SoundInfo *SoundInfo)
{
struct BufferInfo *BufferInfo;
/* Allocate sound buffers. */
if(BufferInfo = (struct BufferInfo *)AllocVec(4 * sizeof(struct BufferInfo),MEMF_CHIP))
{
struct MsgPort *SoundPort;
/* Allocate an io replyport. */
if(SoundPort = CreateMsgPort())
{
struct IOAudio *SoundControlRequest;
/* Allocate the big sound control request. */
if(SoundControlRequest = (struct IOAudio *)CreateIORequest(SoundPort,sizeof(struct IOAudio)))
{
struct IOAudio *SoundRequestLeft;
/* Allocate the left channel sound request. */
if(SoundRequestLeft = (struct IOAudio *)CreateIORequest(SoundPort,sizeof(struct IOAudio)))
{
struct IOAudio *SoundRequestRight;
/* Allocate the right channel sound request. */
if(SoundRequestRight = (struct IOAudio *)CreateIORequest(SoundPort,sizeof(struct IOAudio)))
{
STATIC UBYTE TwoChannels[] =
{
LEFT0F | RIGHT0F,
LEFT0F | RIGHT1F,
LEFT1F | RIGHT0F,
LEFT1F | RIGHT1F
};
STATIC UBYTE LeftChannel[] =
{
LEFT0F,
LEFT1F
};
STATIC UBYTE RightChannel[] =
{
RIGHT0F,
RIGHT1F
};
UBYTE *AllocationMap;
LONG AllocationSize;
/* Determine the correct channel
* allocation map.
*/
if(SoundInfo -> LeftData)
{
if(SoundInfo -> RightData)
{
AllocationMap = TwoChannels;
AllocationSize = sizeof(TwoChannels);
}
else
{
AllocationMap = LeftChannel;
AllocationSize = sizeof(LeftChannel);
}
}
else
{
AllocationMap = RightChannel;
AllocationSize = sizeof(RightChannel);
}
/* Set up for sound channel allocation. */
SoundControlRequest -> ioa_Request . io_Message . mn_Node . ln_Pri = 80;
SoundControlRequest -> ioa_Request . io_Command = ADCMD_ALLOCATE;
SoundControlRequest -> ioa_Request . io_Flags = ADIOF_NOWAIT | ADIOF_PERVOL;
SoundControlRequest -> ioa_Data = AllocationMap;
SoundControlRequest -> ioa_Length = AllocationSize;
/* Open audio.device, allocating the sound
* channels on the fly.
*/
if(!OpenDevice(AUDIONAME,NULL,SoundControlRequest,NULL))
{
/* Clone the sound control request. */
CopyMem(SoundControlRequest,SoundRequestLeft, sizeof(struct IOAudio));
CopyMem(SoundControlRequest,SoundRequestRight, sizeof(struct IOAudio));
/* Separate the channels. */
SoundRequestLeft -> ioa_Request . io_Unit = (struct Unit *)((ULONG)SoundRequestLeft -> ioa_Request . io_Unit & (LEFT0F | LEFT1F));
SoundRequestRight -> ioa_Request . io_Unit = (struct Unit *)((ULONG)SoundRequestRight -> ioa_Request . io_Unit & (RIGHT0F | RIGHT1F));
/* Replay the sound... */
ReplaySound(SoundInfo,SoundControlRequest,SoundRequestLeft,SoundRequestRight,BufferInfo);
/* Do the big cleanup. */
CloseDevice(SoundControlRequest);
}
DeleteIORequest(SoundRequestRight);
}
DeleteIORequest(SoundRequestLeft);
}
DeleteIORequest(SoundControlRequest);
}
DeleteMsgPort(SoundPort);
}
FreeVec(BufferInfo);
}
}
/* SoundLoad(WORD Sound,BYTE Warn):
*
* Loads or frees a sound slot.
*/
STATIC BYTE __regargs
SoundLoad(WORD Sound,BYTE Warn)
{
struct SoundInfo **Info = &SoundSlot[Sound];
STRPTR Name;
BYTE Success = FALSE;
/* Which sound file name are we to pick? */
switch(Sound)
{
case SOUND_BELL:
Name = SoundConfig . BellFile;
break;
case SOUND_CONNECT:
Name = SoundConfig . ConnectFile;
break;
case SOUND_DISCONNECT:
Name = SoundConfig . DisconnectFile;
break;
case SOUND_GOODTRANSFER:
Name = SoundConfig . GoodTransferFile;
break;
case SOUND_BADTRANSFER:
Name = SoundConfig . BadTransferFile;
break;
case SOUND_RING:
Name = SoundConfig . RingFile;
break;
case SOUND_VOICE:
Name = SoundConfig . VoiceFile;
break;
}
/* Is a file name available? */
if(Name[0])
{
/* Sound slot already initialized? */
if(*Info)
{
/* Did the file name change? */
if(Stricmp((*Info) -> Name,Name))
{
/* Free the sound slot. */
FreeSound(*Info);
/* Try to load the sound slot. */
if(*Info = LoadSound(Name,Warn))
{
/* Remember the file name. */
strcpy((*Info) -> Name,Name);
Success = TRUE;
}
}
else
Success = TRUE;
}
else
{
/* Try to load the sound slot. */
if(*Info = LoadSound(Name,Warn))
{
/* Remember the file name. */
strcpy((*Info) -> Name,Name);
Success = TRUE;
}
}
}
else
{
/* Does the slot still contain any sound? */
if(*Info)
{
/* Free the slot. */
FreeSound(*Info);
*Info = NULL;
}
Success = TRUE;
}
return(Success);
}
/* SoundServer(VOID):
*
* Replay a sound.
*/
STATIC VOID __saveds
SoundServer(VOID)
{
LONG Sound = (LONG)SysBase -> ThisTask -> tc_UserData;
/* Get exclusive access to the sound slots. */
ObtainSemaphore(SoundSemaphore);
/* Replay the sound. */
PlaySound(SoundSlot[Sound]);
Forbid();
/* Release access to the sound slots. */
ReleaseSemaphore(SoundSemaphore);
/* Remove ourselves. */
RemTask(NULL);
}
/* SoundExit():
*
* Free allocated sound resources.
*/
VOID
SoundExit()
{
WORD i;
/* Access semaphore available? */
if(SoundSemaphore)
{
ObtainSemaphore(SoundSemaphore);
ReleaseSemaphore(SoundSemaphore);
FreeVec(SoundSemaphore);
SoundSemaphore = NULL;
}
/* Free the slots. */
for(i = 0 ; i < SOUND_COUNT ; i++)
{
if(SoundSlot[i])
{
FreeSound(SoundSlot[i]);
SoundSlot[i] = NULL;
}
}
}
/* SoundInit():
*
* Allocate resources required for sound replaying.
*/
VOID
SoundInit()
{
/* Sound access semaphore available? */
if(SoundSemaphore)
{
ObtainSemaphore(SoundSemaphore);
ReleaseSemaphore(SoundSemaphore);
}
else
{
if(SoundSemaphore = (struct SignalSemaphore *)AllocVec(sizeof(struct SignalSemaphore),MEMF_ANY | MEMF_PUBLIC))
InitSemaphore(SoundSemaphore);
}
/* Preload sounds if necessary. */
if(SoundSemaphore && SoundConfig . Preload)
{
WORD i;
for(i = 0 ; i < SOUND_COUNT ; i++)
SoundLoad(i,TRUE);
}
}
/* SoundPlay(WORD Sound):
*
* Play a certain sound slot.
*/
VOID
SoundPlay(WORD Sound)
{
/* Sound access semaphore available? */
if(SoundSemaphore)
{
/* If to replay a bell sound, check to see
* if we are currently playing a different
* sound.
*/
if(Sound == SOUND_BELL)
{
if(!AttemptSemaphore(SoundSemaphore))
return;
}
else
ObtainSemaphore(SoundSemaphore);
ReleaseSemaphore(SoundSemaphore);
/* Release any other sound if necessary. */
if(!SoundConfig . Preload)
{
WORD i;
for(i = 0 ; i < SOUND_COUNT ; i++)
{
if(i != Sound && SoundSlot[i])
{
FreeSound(SoundSlot[i]);
SoundSlot[i] = NULL;
}
}
}
/* Load the sound slot. */
SoundLoad(Sound,FALSE);
/* Slot filled? */
if(SoundSlot[Sound])
{
struct Task *SoundTask;
LONG Pri = ThisProcess -> pr_Task . tc_Node . ln_Pri + 10;
/* Priority raised, check for limit. */
if(Pri > 127)
Pri = 127;
Forbid();
/* Fire off the sound server task. */
if(SoundTask = CreateTask("term sound task",Pri,SoundServer,4096))
SoundTask -> tc_UserData = (APTR)Sound;
Permit();
}
else
Beep();
}
else
Beep();
}